/*
 * PixelCalcBase.cpp
 *
 *  Created on: Jun 15, 2013
 *      Author: sascha
 */
#include "stdafx.h"
#include "PixelCalcBase.h"

PixelCalcBase::PixelCalcBase(PixelStorage &pS) {
	area = 0;
	diameter = 0;
	perimeter = 0;
	Ru = 0;
	enclosingSize = 0;
	ratioCircle = 0;
	ratioRect = 0;
	solidity = 0;
	hullArea = 0;
	closed = true;
	radius = 0;

	pS.imagePath = "tests/Objekte5.tif";
	pS.grayImage = imread(pS.imagePath, 0);
}

PixelCalcBase::~PixelCalcBase() {
}

/*
 * Schwellenwertberechnung
 * Setze ein Zeitfenster um aktuellen Pixel
 * Zeitfenster (100x100) wird pro Durchlauf um eine Zeile/Spalte verschoben
 */
void PixelCalcBase::raster(PixelStorage &pS) {
	for (int i = 0; i < (pS.grayImage.rows - 50); i++) {
		for (int j = 0; j < (pS.grayImage.cols - 50); j++) {
			pS.pixel = pS.grayImage.at<uchar>(i, j);

			pS.posY2 = i + 50;
			pS.posX2 = j + 50;

			if ((i - 50) >= 0) {
				pS.posY1 = i - 50;
			} else {
				pS.posY1 = 0;
			}
			if ((j - 50) >= 0) {
				pS.posX1 = j - 50;
			} else {
				pS.posX1 = 0;
			}
			minMaxHue(pS, i, j);
		}
	}
}

/*
 * Zeitfenster, min und max Wert berechnen
 * => Matrix Element auf 1 oder 0 setzen
 */
void PixelCalcBase::minMaxHue(PixelStorage &pS, int i, int j) {
	pS.dx = pS.posX2 - pS.posX1;
	pS.dy = pS.posY2 - pS.posY1;

	Mat roiGrayImage(pS.grayImage, Rect(pS.posX1, pS.posY1, pS.dx, pS.dy));
	minMaxLoc(roiGrayImage, &pS.minVal, &pS.maxVal, 0, 0);

	if (pS.pixel.val[0] >= ((pS.minVal + pS.maxVal) / 2)) {
		pS.binMatrix.at<int>(i, j) = 1;
	} else {
		pS.binMatrix.at<int>(i, j) = 0;
	}
}

/* Konturen von Objekten erkennen
 * und Werte der Konturen in einer 2 Ebenen Hierarchie speichern,
 * findContours(); arbeitet mit 8 Bit Single Channel Binaerbild
 */
void PixelCalcBase::contours(PixelStorage &pS) {
	findContours(pS.binMatrix, pS.objContours, CV_RETR_CCOMP,
			CV_CHAIN_APPROX_SIMPLE);

	cout << "Contour Count: " << pS.objContours.size() << endl;
}

void PixelCalcBase::drawTrueContours(PixelStorage &pS) {
	int size = pS.trueContours.size();

	Scalar color = Scalar(0, 0, 255);

	for (int i = 0; i < size; i++) {
		drawContours(pS.contoursMatrix, pS.trueContours, i, color, 0, 8);
	}
}

/*
 * "Unechte" Konturen herausfiltern, indem auf Momente wie
 Flaeche (area), Umfang (perimeter) und Geometrie (Ru)
 geprüft wird
 *
 * HuMoments berechnen, um gleiche Objekte zu erkennen,
 * die sich nur in Rotation, Spiegelung und/oder Skalierung unterscheiden
 */
void PixelCalcBase::calcTrueContours(PixelStorage &pS) {
	hull.resize(pS.objContours.size());
	defects.resize(pS.objContours.size());

	int nContours = pS.objContours.size();

		for (int i = 0; i < nContours; i++) {
			m = moments(pS.objContours[i]);
			HuMoments(m, getMoments);

			perimeter = cv::arcLength(pS.objContours[i], closed);
			approxPolyDP(pS.objContours[i], approx, 0.1 * perimeter, closed);

			/* Mehr Konturen mit "approx",
			 * Linien/Raender sind aber gefiltert
			 */
			area = cv::contourArea(approx);

			/* besseres Resultat, wenn "area" nicht direkt an "Ru" zugewiesen,
			 sondern erst "diameter" berechnen*/
			diameter = sqrt((4 * area) / M_PI);
			Ru = (diameter * M_PI) / pow(perimeter, 2);

			//Size prüfen, sonst Assertion Fehler
			if (pS.objContours[i].size() > 3) {
				convexHull(pS.objContours[i], hull[i]);
				convexityDefects(pS.objContours[i], hull[i], defects[i]);
			}

			/*Ru bei Objekte5.tif nicht notwendig, aber bei objekte.tif ja
			 um eindeutige Nichtpunkte herauszufiltern*/
			if ((Ru <= 0.1) && (Ru > 0.01)) {
				if (defects[i].size() == 0) {

					/*Verhaeltnis Groeße des minimalen Kreises um einen Farbklecks
					 zu der Flaeche des Farbklecks' selbst*/
					minEnclosingCircle(approx, center, radius);
					enclosingSize = M_PI * pow(radius, 2);
					ratioCircle = enclosingSize / area;

					//Extent
					rect = boundingRect(pS.objContours[i]);
					ratioRect = area / (rect.width * rect.height);

					//Solidity
					hullArea = cv::contourArea(pS.objContours[i]);
					solidity = area / hullArea;

					//manche Farbkleckse haben groeßere Flaeche als "echte" Farbkleckse

					if (ratioCircle <= 3) {
						/*objekt.tif erkennt Klecks _nur_ wegen Extent nicht (ca. 0.5);
						 liegt daher außerhalb*/
						if ((ratioRect >= 0.33) && (ratioRect <= 0.40)) {
							if (solidity >= 0.7) {

								/*central und spatial moments
								 if (m.mu02 >= 10.5) {
								 if ((m.m00 > 10) && (m.m00 < 18)) {*/

								/*die zweite Invariante der HuMomente ergibt
								 besseres Ergebnis als central und spatial moments;
								 nur 11 "unechte" anstatt 26 "unechte" Konturen*/
								if (getMoments[1] <= 0.0017) {
									/*cout << "data0: " << getMoments[0] << endl;*/
									pS.trueContours.push_back(pS.objContours[i]);
								}
							}
						}

					}

				}
			}
		}
		/*cout << "Contour Count: " << pS.trueContours.size() << endl;*/
		drawTrueContours(pS);
}

void PixelCalcBase::loadImage(Mat image) {
	try {
		namedWindow("Mustererkennung", CV_WINDOW_NORMAL);

		imshow("Mustererkennung", image);
	} catch (Exception &e) {
		/*cout << "\"Show Image\" Exception: " << e.err << endl;*/
	}
}
